package com.shiyi.shiyiaicodefather.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.shiyi.shiyiaicodefather.ai.AiCodeGenTypeRoutingService;
import com.shiyi.shiyiaicodefather.ai.AiCodeGenTypeRoutingServiceFactory;
import com.shiyi.shiyiaicodefather.ai.AiCodeGeneratorService;
import com.shiyi.shiyiaicodefather.constant.AppConstant;
import com.shiyi.shiyiaicodefather.core.AiCodeGeneratorFacade;
import com.shiyi.shiyiaicodefather.core.builder.VueProjectBuilder;
import com.shiyi.shiyiaicodefather.core.handle.StreamHandlerExecutor;
import com.shiyi.shiyiaicodefather.exception.BusinessException;
import com.shiyi.shiyiaicodefather.exception.ErrorCode;
import com.shiyi.shiyiaicodefather.exception.ThrowUtils;
import com.shiyi.shiyiaicodefather.model.dto.app.AppAddRequest;
import com.shiyi.shiyiaicodefather.model.dto.app.AppQueryRequest;
import com.shiyi.shiyiaicodefather.model.entity.App;
import com.shiyi.shiyiaicodefather.mapper.AppMapper;
import com.shiyi.shiyiaicodefather.model.entity.User;
import com.shiyi.shiyiaicodefather.model.enums.ChatHistoryMessageTypeEnum;
import com.shiyi.shiyiaicodefather.model.enums.CodeGenTypeEnum;
import com.shiyi.shiyiaicodefather.model.vo.AppVO;
import com.shiyi.shiyiaicodefather.model.vo.UserVO;
import com.shiyi.shiyiaicodefather.service.AppService;
import com.shiyi.shiyiaicodefather.service.ChatHistoryService;
import com.shiyi.shiyiaicodefather.service.ScreenshotService;
import com.shiyi.shiyiaicodefather.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author 十一
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App>  implements AppService{

  @Resource
  private UserService userService;

  @Resource
  private AiCodeGeneratorFacade aiCodeGeneratorFacade;

  @Resource
  private ChatHistoryService chatHistoryService;

  @Resource
  private StreamHandlerExecutor streamHandlerExecutor;

  @Resource
  private VueProjectBuilder vueProjectBuilder;

  @Resource
  private ScreenshotService screenshotService;

  @Resource
  private AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService;

  @Override
  public AppVO getAppVO(App app) {
    if (app == null) {
      return null;
    }
    AppVO appVO = new AppVO();
    BeanUtil.copyProperties(app, appVO);
    // 关联查询用户信息
    Long userId = app.getUserId();
    if (userId != null) {
      User user = userService.getById(userId);
      UserVO userVO = userService.getUserVO(user);
      appVO.setUser(userVO);
    }
    return appVO;
  }

  @Override
  public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
    if (appQueryRequest == null) {
      throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
    }
    Long id = appQueryRequest.getId();
    String appName = appQueryRequest.getAppName();
    String cover = appQueryRequest.getCover();
    String initPrompt = appQueryRequest.getInitPrompt();
    String codeGenType = appQueryRequest.getCodeGenType();
    String deployKey = appQueryRequest.getDeployKey();
    Integer priority = appQueryRequest.getPriority();
    Long userId = appQueryRequest.getUserId();
    String sortField = appQueryRequest.getSortField();
    String sortOrder = appQueryRequest.getSortOrder();
    return QueryWrapper.create()
      .eq("id", id)
      .like("appName", appName)
      .like("cover", cover)
      .like("initPrompt", initPrompt)
      .eq("codeGenType", codeGenType)
      .eq("deployKey", deployKey)
      .eq("priority", priority)
      .eq("userId", userId)
      .orderBy(sortField, "ascend".equals(sortOrder));
  }

  @Override
  public List<AppVO> getAppVOList(List<App> appList) {
    if (CollUtil.isEmpty(appList)) {
      return new ArrayList<>();
    }
    // 批量获取用户信息，避免 N+1 查询问题
    Set<Long> userIds = appList.stream()
      .map(App::getUserId)
      .collect(Collectors.toSet());
    Map<Long, UserVO> userVOMap = userService.listByIds(userIds).stream()
      .collect(Collectors.toMap(User::getId, userService::getUserVO));
    return appList.stream().map(app -> {
      AppVO appVO = getAppVO(app);
      UserVO userVO = userVOMap.get(app.getUserId());
      appVO.setUser(userVO);
      return appVO;
    }).collect(Collectors.toList());
  }

  @Override
  public Flux<String> chatToGenCode(Long appId, String message, User loginUser) {
    ThrowUtils.throwIf(appId == null,ErrorCode.PARAMS_ERROR,"应用ID不能为空");
    ThrowUtils.throwIf(StrUtil.isBlank(message),ErrorCode.PARAMS_ERROR,"用户消息不能为空");

    App app = this.getById(appId);
    ThrowUtils.throwIf(app == null,ErrorCode.PARAMS_ERROR,"应用不存在");

    String codeGenType = app.getCodeGenType();
    CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
    if (codeGenTypeEnum == null) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR,"不支持的应用类型");
    }

    //通过校验后，添加用户消息到对话历史
    boolean result = chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(),loginUser.getId());
    //调用AI生成代码（流式）
    Flux<String> contentFlux = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);

    //收集 AI相应内容并在完成后同步到对话历史
    return streamHandlerExecutor.doExecute(contentFlux,chatHistoryService,appId,loginUser,codeGenTypeEnum);

  }

  /**
   * 部署应用
   * @param appId
   * @param loginUser
   * @return
   */
  @Override
  public String deployApp(Long appId, User loginUser) {
    // 1. 参数校验
    ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
    ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
    // 2. 查询应用信息
    App app = this.getById(appId);
    ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
    // 3. 验证用户是否有权限部署该应用，仅本人可以部署
    if (!app.getUserId().equals(loginUser.getId())) {
      throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限部署该应用");
    }
    // 4. 检查是否已有 deployKey
    String deployKey = app.getDeployKey();
    // 没有则生成 6 位 deployKey（大小写字母 + 数字）
    if (StrUtil.isBlank(deployKey)) {
      deployKey = RandomUtil.randomString(6);
    }
    // 5. 获取代码生成类型，构建源目录路径
    String codeGenType = app.getCodeGenType();
    String sourceDirName = codeGenType + "_" + appId;
    String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
    // 6. 检查源目录是否存在
    File sourceDir = new File(sourceDirPath);
    if (!sourceDir.exists() || !sourceDir.isDirectory()) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用代码不存在，请先生成代码");
    }
    CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
    if (codeGenTypeEnum == CodeGenTypeEnum.VUE_PROJECT) {
      boolean buildSuccess = vueProjectBuilder.buildProject(sourceDirPath);
      ThrowUtils.throwIf(!buildSuccess,ErrorCode.OPERATION_ERROR, "Vue 构建项目失败,请检查代码和依赖");
      File distDir = new File(sourceDirPath, "dist");
      ThrowUtils.throwIf(!distDir.exists(),ErrorCode.SYSTEM_ERROR,"Vue 构建项目完成但未生成dist目录");
      sourceDir = distDir;
      log.info("Vue 项目部署成功，将部署dist目录{}",distDir.getAbsolutePath());
    }

    // 7. 复制文件到部署目录
    String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
    try {
      FileUtil.copyContent(sourceDir, new File(deployDirPath), true);
    } catch (Exception e) {
      throw new BusinessException(ErrorCode.SYSTEM_ERROR, "部署失败：" + e.getMessage());
    }


    // 8. 更新应用的 deployKey 和部署时间
    App updateApp = new App();
    updateApp.setId(appId);
    updateApp.setDeployKey(deployKey);
    updateApp.setDeployedTime(LocalDateTime.now());
    boolean updateResult = this.updateById(updateApp);
    ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用部署信息失败");
    // 9. 返回可访问的 URL
    String appDeployUrl  = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
    //异步生成截图并更新应用封面
    generateAppScreenshotAsync(appId,appDeployUrl);
    return appDeployUrl ;
  }

  @Override
  public Long createApp(AppAddRequest appAddRequest, User loginUser) {

    // 参数校验
    String initPrompt = appAddRequest.getInitPrompt();
    ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");

    // 构造入库对象
    App app = new App();
    BeanUtil.copyProperties(appAddRequest, app);
    app.setUserId(loginUser.getId());
    // 应用名称暂时为 initPrompt 前 12 位
    app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
    // 暂时设置为多文件生成
    CodeGenTypeEnum codeGenTypeEnum = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
    app.setCodeGenType(codeGenTypeEnum.getValue());
    // 插入数据库
    boolean result = this.save(app);
    ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    log.info("创建应用成功, ID: {},应用类型：{}", app.getId(),codeGenTypeEnum.getValue());
    return app.getId();
  }

  /**
   * 异步生成截图并更新封面
   * @param appId 应用id
   * @param appDeployUrl 应用部署url
   */
  private void generateAppScreenshotAsync(Long appId, String appDeployUrl) {
    Thread.startVirtualThread(() -> {
      String screenshotUrl = screenshotService.generateAndUploadScreenshot(appDeployUrl);
      App updateApp = new App();
      updateApp.setId(appId);
      updateApp.setCover(screenshotUrl);
      boolean result = this.updateById(updateApp);
      ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "更新应用封面失败");
    });
  }


  /**
   * 删除应用时关联删除对话历史
   *
   * @param id 应用ID
   * @return 是否成功
   */
  @Override
  public boolean removeById(Serializable id) {
    if (id == null) {
      return false;
    }
    // 转换为 Long 类型
    Long appId = Long.valueOf(id.toString());
    if (appId <= 0) {
      return false;
    }
    // 先删除关联的对话历史
    try {
      chatHistoryService.deleteByAppId(appId);
    } catch (Exception e) {
      // 记录日志但不阻止应用删除
      log.error("删除应用关联对话历史失败: {}", e.getMessage());
    }
    // 删除应用
    return super.removeById(id);
  }


}
